package eu.gegenleitner.esspacc.ca;

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;

import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.misc.NetscapeCertType;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX500NameUtil;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import com.sun.org.apache.xml.internal.security.utils.Base64;

/**
 * Codeexamples taken from 
 * https://www.mayrhofer.eu.org/create-x509-certs-in-java
 * http://stackoverflow.com/questions/22289124/how-to-programmatically-generate-a-client-authentication-certificate
 * 
 * @author Martin
 *
 */
public class Certifier {
	
	public Certifier() {
		Security.addProvider(new BouncyCastleProvider());
	}

	public String generateCertFromCSR(CSRChecker certificateValues, String pathToKeyStore) throws IOException,
																			NoSuchAlgorithmException,
																			OperatorCreationException,
																			CertificateException,
																			InvalidKeyException,
																			NoSuchProviderException,
																			SignatureException,
																			KeyStoreException,
																			UnrecoverableKeyException {
		KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
		keystore.load(new FileInputStream(pathToKeyStore), "password".toCharArray());
		PrivateKey caKey = (PrivateKey)keystore.getKey("1", "password".toCharArray());
		X509Certificate caCert = (X509Certificate)keystore.getCertificate("1");
		
		X500Name subject = new X500Name("CN=\""+certificateValues.getGivenName() + " " + certificateValues.getSurName()
				+"\","
				+ "OU=\"ESSPACC\","
				+ "O=\"FHOOE\","
				+ "L=\"Hagenberg\","
				+ "ST=\"Upper Austria\","
				+ "C=\"AT\"");
		X500Name issuerName = JcaX500NameUtil.getIssuer(caCert);
		Date startDate = new Date();
		Calendar c = Calendar.getInstance();
		c.setTime(startDate);
		c.add(Calendar.YEAR, 5);
		Date endDate = c.getTime();
		String subjAltNameURI = certificateValues.getMailAddress();
		BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
		
//		NetscapeCertRequest netscapeCertReq = new NetscapeCertRequest(certificateValues.getPublicKey().getEncoded());
		PublicKey pubKey = certificateValues.getPublicKey();
		
		X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(issuerName,
				serialNumber,
				startDate,
				endDate,
				subject,
				SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()));
		// Set Key to NO-Ca
		certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
		
		// Define KeyUsage
		certBuilder.addExtension(Extension.keyUsage, true,
				new KeyUsage(KeyUsage.digitalSignature | KeyUsage.nonRepudiation |
						KeyUsage.keyEncipherment | KeyUsage.keyAgreement));
		// Add S/MIME and SSL-Capability
		certBuilder.addExtension(MiscObjectIdentifiers.netscapeCertType, true, new NetscapeCertType(NetscapeCertType.sslClient | NetscapeCertType.smime));
		
		// Add Subject key identifier extension
		SubjectKeyIdentifier subKeyIdentifier = new JcaX509ExtensionUtils().createSubjectKeyIdentifier(pubKey);
		certBuilder.addExtension(Extension.subjectKeyIdentifier, false, subKeyIdentifier);
		
		// Add Subject Alternative Name
		GeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.rfc822Name, subjAltNameURI));
		certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
		
		// Sign the new certificate with the CA's private Key
		ContentSigner signer =  new JcaContentSignerBuilder("SHA512withRSA").setProvider("BC").build(caKey);
		X509CertificateHolder holder = certBuilder.build(signer);
		X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder);
		
		cert.verify(caCert.getPublicKey());
		
		String newCertBase64 = Base64.encode(cert.getEncoded());
		String caCertBase64 = Base64.encode(caCert.getEncoded());
		String retval = "<caCert>"
					+ caCertBase64
					+ "</caCert>"
					+ "<newCert>"
					+ newCertBase64
					+ "</newCert>";
		return retval;
	}

}
